<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
  <head>
    <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type">
    <title>Cofe3</title>
  </head>
  <body>
    <h2>Introduction</h2>
    <img style="width: 904px; height: 438px;" alt="WhatIsCofe" src="WhatIsCofe.jpg"><br>
    Cofe3 is a set of libraries that enable developers to build their home cloud
    services for their specific kind of entries, which enable user to host them
    on their own computer, and using it to serve their own thin client computer
    like smart phone and tablet via the web browser or Apps.&nbsp; This library
    is released under <strong>LGPL</strong> license.<br>
    <br>
    Cofe3 is a developer friendly implementation, the Core library is similar to
    System.IO.FileSystemInfo, except the entry is now an interface called
    Cofe.Core.ICofeSystemInfo (entries).&nbsp; Developer can choose to develop
    their own entries, or they can add new properties or action to existing
    entries, either way will make them storable in database, searchable and
    accessable on the web, like the IFileSystemInfo in the Cofe.IO
    library.&nbsp; But please keep in mind that behind the scene the
    implementation of Cofe3 is much different that System.IO, one should not
    think doing a custom implementation as effortless as inheriting a class.<br>
    <br>
    CofeWs3 uses ASP.Net MVC3 to host the server, which provides Authentication
    support, accessing the API require at least a user ([Authorize] tag), and
    there are volume permission and user role check on the Cofe side.&nbsp; The
    configuration is and will be developed as MVC web page, but accessing the
    CofeSystem will not.&nbsp; The communication between CofeWs3 and user thin
    client is REST based API, which is mainly through JSON, and Atom10 for
    events, instead of ViewBag.&nbsp; This allow third-party Apps to be
    developed.<br>
    <br>
    CofeDb3 is Entity Framework Code First based, this allow third-party to plug
    in their entry table for CofeSystemInfos, developer can choose to save
    properties in PropertyPairs table (e.g. PropertyPair_Int32 for int values)
    as well.&nbsp; Regardless which way to cache data, both way allow user to
    search their entire collection using FilterString, which is similar to
    search string in search engine.<br>
    <br>
    <h2>Background</h2>
    This project have went through a number of iterations.&nbsp; <br>
    <br>
    What Cofe1 does was to mash archives (supported by Cake and then
    SevenZipSharp) to file system (see DirectoryInfoEx), which allow
    DirectoryInfo like browsing for archives, it's one of the major part of my
    zippware program.&nbsp; Then after I completed FileExplorer2, I have started
    working on Cofe2. which is actually my dissertation project.&nbsp; The
    earlier version of Cofe2 is very primitive, it uses hard coded sql to
    communicate with the database, and my own JSON to Xml (and vice versa)
    converter.The final version of Cofe2&nbsp; uses Entity framrwork model
    first, support files only, with only text based security support.<br>
    <br>
    The main reason for scrapping Cofe2 for Cofe3 is to uses the latest
    framework to do something that I couldn't, namely the security in MVC3 and
    non-hardcoded-entries using EF Code First, to support actions/tasks as
    properties, which allow calling in WebAPI, using GET (e.g.
    CofeDirectoryProperties.List) or DELETE (e.g. CofeProperties.DeleteAsync)
    action, and to support non-file entries through Code First.<br>
    <br>
    I want to emphasis that since the earlier stage of Cofe2, the project is
    released under LGPL license on CodePlex.&nbsp; Home cloud storage do access
    user's most sensitive data, so my initial thought is that it has to give
    confidence, confidence for the user to know what they are running, and it
    shouldn't be used to take unfair advantage to the user.<br>
    <br>
    I try to make the web service as secure as I can, but as I am not expert on
    it so there may be some insecure code, please report me if you found any.<br>
    <br>
    <br>
    <h2> Uncompleted items</h2>
    Even after so long developement, there's still some important but
    uncompleted items, here's a list :<br>
    <h4>Separate Database updating code in Web.Services to a Windows Services -
      important</h4>
    <p>Currently, the Cofe.Web.Services create three threads when started, two
      ActiveUpdater for updating the expired entry and listing in database, and
      one+ event source to listen System.IO.FileSystemEvents and trigger
      Cofe.Core.CofeSystemEvents.&nbsp; The problem is that when ASP.Net refresh
      the AppDomain, for various reasons, it destroy my threads.&nbsp; To solve
      the problem I will needed either to separate the database updating code
      and make it Windows Services or Console Application, or using the WebAPI
      Self-Host.</p>
    <h4>Fix the WinRT implementation.</h4>
    Earlier build of Cofe have WinRT support in Cofe.Core and Cofe.IO (No
    EntityFramework.RT, so no Cofe.Data.RT), but I have stopped updated after
    working on WebServices.&nbsp; Currently the build is not compilable.&nbsp;
    Hopefully I can make both library works again, and add Cofe.Media.RT
    library.<br>
    <h4>Improve Html5 client / Add WPF client</h4>
    Currently the Html5 client does not automatically update when there's a
    change in cofe system.&nbsp; It can receive cofe system change events from
    feeds, but it doesn't update the web ui.<br>
    <h4>Script </h4>
    Cofe does provide IScriptServices to parse and run script, but it's only use
    is creating events feed in CofeWs. I am looking to extend it to support the
    CofeSystem.<br>
    <br>
    <h2> </h2>
    <h2>Index</h2>
    This article willl be divided into 2 parts, the first will be a quick look
    for the features Cofe3 provides, and how to use it. &nbsp; In the second
    part I will try to explain briefly how it works internally.<br>
    <br>
    Part I<br>
    <ul>
      <li>Libraries</li>
      <ul>
        <li>Cofe3 - Cofe.Core, Cofe.IO and Cofe.Media<br>
        </li>
        <li>CofeDb3 - Cofe.Data <br>
        </li>
        <li>CofeWs3 - Cofe.Web and Cofe.Web.Services</li>
      </ul>
      <li>Cofe as a general library</li>
      <ul>
        <li>Configuration - CofeSettings and volume registering<br>
        </li>
        <li>Entries - Parsing, casting and working with it<br>
        </li>
        <li>Searching - and building custom entry list<br>
        </li>
      </ul>
      <li>Cofe as a web services</li>
      <ul>
        <li>Admin - assigning roles and permissions<br>
        </li>
        <li>APIs - exists REST web apis<br>
        </li>
        <li>Explore - Html5 client for navigating the cofe system<br>
        </li>
      </ul>
    </ul>
    <br>
    <h2>Libraries</h2>
    <br>
    <table  border="1"

      cellpadding="2" cellspacing="2">
      <tbody>
        <tr>
          
          <td ><strong>Category</strong><br>
          </td>
          <td ><strong>Library</strong><br>
          </td>
          <td ><strong>Description</strong><br>
          </td>
          <td ><strong>Notable types</strong><br>
          </td>
        </tr>
        <tr>
          <td colspan="1" rowspan="3" >Cofe3<br>
            <br>
          </td>
          <td >Cofe.Core<br>
          </td>
          <td >Main library, provide
            all services so the CofeSystem can work, it also include the root
            interface for all entries (ICofeSystemInfo), and several other
            interfaces (ICofeItemInfo, ICofeDirectoryInfo)<br>
          </td>
          <td >ICofeSystemInfo,
            IEntryTypeInfo<br>
            ICofeItemInfo, ICofeDirectoryInfo<br>
            ICofeStreamInfo<br>
          </td>
        </tr>
        <tr>
          <td >Cofe.IO<br>
          </td>
          <td >System.IO
            implementation for Cofe, added support to IFileSystemInfo<br>
          </td>
          <td >IFileSystemInfo<br>
            IFileInfo, IDirectoryInfo<br>
          </td>
        </tr>
        <tr>
          <td >Cofe.Media<br>
          </td>
          <td >Media implementation
            for Cofe, currently only parse exif tags from jpg images,<br>
            provides IMediaInfo and IImageInfo interface to any entries that
            have name ended with jpg and support
            CofeStreamProperties.OpenStreamAsync.<br>
          </td>
          <td >IMediaInfo<br>
            IImageInfo<br>
          </td>
        </tr>
        <tr>
          <td >CofeDB3</td>
          <td >Cofe.Data<br>
          </td>
          <td >Cache entries and their
            properties in database, translate search string (EntryFiltersString)
            to SQL statement to do the query.<br>
          </td>
          <td >EFRepository
            (IRepository)<br>
            IActiveDatabaseUpdater<br>
          </td>
        </tr>
        <tr>
          <td colspan="1" rowspan="2" >CofeWS3</td>
          <td >Cofe.Web<br>
          </td>
          <td >Provide the
            implementation of the Web Services, including event updating, entry
            formatting <br>
          </td>
          <td >IFeedServices<br>
            IHttpRequestFormatterManager<br>
          </td>
        </tr>
        <tr>
          <td >Cofe.Web.Services<br>
          </td>
          <td >Include the
            APIController to provide the web api and the javascripts to access
            the api.<br>
          </td>
          <td >EntryController<br>
            ParseController<br>
          </td>
        </tr>
      </tbody>
    </table>
    <br>
    <br>
    <br>
    <h2>Cofe as a general library</h2>
    <h3>LinqPad</h3>
    <p>There's a number of linq files in \doc\LinqPad\Tutorial for demo, which I
      mainly use for debugging and testing.&nbsp; <br>
      The database related linq files complains about migration, it's caused by
      Cofe.Media is not loinked in those Linq files, running the linq again to
      fix the problem.</p>
    <h3>Configuration</h3>
    There are two things that you have to configure Cofe3 before use, one is to
    determine which library to use, and then register the volume.<br>
    <br>
    <h4>Register Library</h4>
    To register a library, one can use ModuleBootStrapper, all library contains
    a RegisterModule class in their namespace, for examples,
    Cofe.IO.RegisterModule in Cofe.IO, you have to include all RegisterModules
    in the constructor of MofuleBootStrapper, e.g. (the sequence of
    registerModule doesn't matter)<br>
    <br>
    <span>&nbsp;new ModuleBootStrapper(<br>
      &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;true, <br>
      &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new
      Cofe.Core.RegisterModule(),<br>
      &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new
      Cofe.IO.RegisterModule()<br>
      &nbsp;&nbsp;&nbsp;&nbsp;).Run();&nbsp;&nbsp;&nbsp;&nbsp; <br>
      <br>
      The above examples include only Cofe and IO, this minimial for Cofe3 to
      work, you can register volume provided by IO, no database support in this
      case, searching will be done by iterating sub-directories.<br>
    </span><br>
    <span>new ModuleBootStrapper(<br>
      &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;true, <br>
      &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new
      Cofe.Core.RegisterModule(),<br>
      &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new
      Cofe.Data.RegisterModule(true, CacheUpdateOptions.Manual),<br>
      &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new
      Cofe.IO.RegisterModule(),<br>
      &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new
      Cofe.Media.RegisterModule()<br>
      &nbsp;&nbsp;&nbsp;&nbsp;).Run();&nbsp;&nbsp;&nbsp;&nbsp; <br>
    </span><br>
    The above examples added Data and Media, you can now "cast" files as
    IImageInfo using the .As&lt;I&gt;() method, and the database support is now
    included, noticed that&nbsp; CacheUpdateOptions is set to Manual.<br>
    <br>
    <table style="text-align: left; width: 798px;" border="1" cellpadding="2" cellspacing="2">
      <tbody>
        <tr>
          <td style="vertical-align: top; width: 71px;"><strong>CacheUpdateOptions</strong></td>
          <td style="vertical-align: top; width: 141px;"><strong>Purpose</strong><br>
          </td>
          <td style="vertical-align: top; width: 559px;"><strong>Description</strong><br>
          </td>
        </tr>
        <tr>
          <td style="vertical-align: top; width: 71px;">Manual <br>
          </td>
          <td style="vertical-align: top; width: 141px;">Test<br>
          </td>
          <td style="vertical-align: top; width: 559px;">User have to manually
            called DatabaseProperties.WriteRepositoryAsync to update the
            database.<br>
          </td>
        </tr>
        <tr>
          <td style="vertical-align: top; width: 71px;">Passive<br>
          </td>
          <td style="vertical-align: top; width: 141px;">Desktop<br>
          </td>
          <td style="vertical-align: top; width: 559px;">Update automatically
            when user parse or list an entry.<br>
          </td>
        </tr>
        <tr>
          <td style="vertical-align: top; width: 71px;">Active<br>
          </td>
          <td style="vertical-align: top; width: 141px;">Web Service<br>
          </td>
          <td style="vertical-align: top; width: 559px;">Create background
            threads to look for expired entry or listing and update in
            background.<br>
          </td>
        </tr>
      </tbody>
    </table>
    <br>
    <br>
    <h4>Register Volume</h4>
    Volume are provided by registered IVolumeFactory(s).&nbsp; Currently only
    usable one is CofeIO provided by Cofe.IO.&nbsp; <br>
    &nbsp;<br>
    To register a CofeIO volume, you have to specify the VolumePath parameter,
    which is the root directory for CofeIO to uses:<br>
    (you can map multiple volume at a time.)<br>
    <br>
    await VolumeFE.MapAsync("CofeIO", "cofe", Tuple.Create("VolumePath",
    "c:\\cofe"));&nbsp;&nbsp;&nbsp; <br>
    <br>
    This will give all user permission to CRUD the directory, for web user, you
    should specify the permission, e.g.<br>
    &nbsp;<br>
    VolumeFE.Unmap("cofe");<br>
    await VolumeFE.MapAsync(PermissionType.None, "CofeIO", "cofe",
    Tuple.Create("VolumePath", "c:\\cofe"));<br>
    VolumeFE.GrantPermission(new string[] { "Admin" }, "cofe",
    PermissionType.All); &nbsp; <br>
    <br>
    VolumeFE.Factories display a list of registered factories, while
    VolumeFE.Volumes return a list of registered volumes. <br>
    <img alt="Volumes" src="file:///E:/volume_linq.png"><br>
    This can be annoying to setup volume and permission each time when
    application start, so one can use CofeSettings class, when you update the
    volume and permission, it updates the
    CofeServices.CofeSettings.Permissions/Volumes as well, and these settings
    can be saved to external file (default external storage),you can then
    restore when application is restarted. <br>
    <br>
    await CofeServices.SaveAsync();<br>
    await CofeServices.RestoreAsync();&nbsp; //Do not call
    CofeServices.CofeSettings.Save/RestoreAsync() directly.<br>
    <img alt="settings" src="settings_linq.jpg"><br>
    <h3>Helper classes</h3>
    <p>There's a number of useful methods in different classes, which can be
      hard to remember.&nbsp; Cofe3 provide a number of static helper classes,
      which called those frequently use methods.&nbsp; These helper class all
      ends with FE.&nbsp; <br>
      <br>
    </p>
    <table border="1" width="100%">
      <tbody>
        <tr>
          <td><strong>Cofe3's Static Helpers</strong></td>
          <td><strong>SystemIO's equiviant</strong></td>
          <td><strong>Description</strong></td>
        </tr>
        <tr>
          <td>Cofe.Core.CofeServices</td>
          <td><br>
          </td>
          <td>CofeServices is a central place to access all services in
            Cofe.&nbsp; ICofeServices is a implemented by all services, so
            developer can call
            CofeServices.ServicesLocater.FindServices&lt;&gt;() to find the
            desired service interface .&nbsp; <br>
            Some of the services included ServicesLocater, EntryConstructor,
            PropertyDictionary, PathParser and EntrySerializer.</td>
        </tr>
        <tr>
          <td style="margin-left: 0px; ">Cofe.Core.PathFE<br>
          </td>
          <td>System.IO.Path</td>
          <td>Some path related utilities.&nbsp; Cofe path contains {} letters,
            which made it incompatible with System.IO.Path.</td>
        </tr>
        <tr>
          <td>Cofe.Core.EntryFE</td>
          <td><br>
          </td>
          <td>Entry (ICofeSystemInfo) related helper functions, include path
            parsing (TryParsePath/Exists), Transfer (Move/Copy/Rename/LinkAsync)
            and Search (SearchAsync)</td>
        </tr>
        <tr>
          <td>Cofe.Core.VolumeFE</td>
          <td>System.IO.Drive</td>
          <td>List and register volume, assign permission to roles.</td>
        </tr>
        <tr>
          <td>Cofe.Core.UserFE</td>
          <td><br>
          </td>
          <td>Assign role to user.</td>
        </tr>
        <tr>
          <td>Cofe.Core.EntryListFE</td>
          <td><br>
          </td>
          <td>EntryList related helper functions, include path parsing and
            EntryList constructing.</td>
        </tr>
        <tr>
          <td>Cofe.Core.LinkFE</td>
          <td><br>
          </td>
          <td>Get or Set entry links.</td>
        </tr>
        <tr>
          <td>Cofe.IO.FileFE<br>
          </td>
          <td>System.IO.File</td>
          <td>File (IFileInfo) related helper functions.</td>
        </tr>
        <tr>
          <td>Cofe.IO.DirectoryFE<br>
          </td>
          <td>System.IO.Directory</td>
          <td>Directory (IDirectoryInfo) related helper functions.</td>
        </tr>
      </tbody>
    </table>
    <h3>Entries</h3>
    Although the smallest piece of data in cofe system is a property pair, an
    entry is the business entity inside cofe system.&nbsp; Depended what you are
    developing, it can represent an IO item (file / directory), a contact of a
    person, or perhaps even a section of a word document.<br>
    <br>
    Once you registered a volume in previous section, it's in the cofe system
    and you can parse it using the path:<br>
    <br>
    ICofeSystemInfo rootEntry = await EntryFE.TryParsePathAsync("{cofe}")<br>
    <br>
    Notices that, rootEntry is in the type ICofeSystemInfo interface.&nbsp;
    ICofeSystemInfo doesn not&nbsp; allow you to list sub-entries, you have to
    convert it to IO.IDirectoryInfo:<br>
    <br>
    IDirectoryInfo rootDirectory = rootEntry.As&lt;IDirectoryInfo&gt;();<br>
    rootDirectory = await
    EntryFE.TryParsePathAsync&lt;IDirectoryInfo&gt;("{cofe}"); //Or specify the
    type directory<br>
    rootDirectory = DirectoryFE.FromDirectoryPathAsync("{cofe}"); //Or using
    DirectoryFE helper class.<br>
    IFileInfo file1 = await FileFE.FromFilePathAsync("{cofe}\\testParse1.txt");<br>
    Console.WriteLine(file1.Length);<br>
    <br>
    If an entry is parsed from EntryFE.TryParsePathAsync(), which returns
    ICofeSystemInfo, the rootEntry is not necessary already implement the <em>IDirectoryInfo
      </em>class, so you have to call the cast <em>As&lt;T&gt;()</em>
    method,&nbsp; Cofe check if the current implementation support the requested
    interface, and re-create the class that support the interface if
    needed.&nbsp; You can use <em>Is&lt;T&gt;()</em> method to see if it can
    "cast" as specified type.<br>
    <br>
    <img alt="entry" src="entry_linq.jpg"><br>
    <br>
    Cofe.IO.IDirectoryInfo inherited from ICofeDirectoryInfo/IFileSystemInfo and
    then ICofeSystemInfo, while Cofe.IO.IFileInfo inherited from
    ICofeItemInfo/IFileSystemInfo, and then ICofeSystemInfo, and System.IO have
    implementation of FileInfo and DirectoryInfo.&nbsp; So to allow one to
    access both IDirectoryInfo and IFileInfo properties of an archive file, one
    have to use the As&lt;T&gt;() method to switch between both interface.&nbsp;
    <br>
    <br>
    All interfaces implements ICofeSystemInfo, ICofeSystemInfo have some
    important properties, Properties and EntryTypeInfo.<br>
    <br>
    Properties property is a IPropertyHost, which is responsible for access of
    all other properties (e.g. length, md5) and actions (e.g. DeleteAsync, List)
    of an entry.&nbsp; When you call DeleteAsync you actually get a Func&lt;pd,
    Task&gt; from a set of PropertyProviders and execute it, this <a href="systemIOPP.jpg">screenshot</a>
    will give you some idea.&nbsp; PropertyHost will be explained in next
    article.&nbsp; <br>
    <br>
    EntryTypeInfo is a property provided by the IPropertyHost, it provides type
    information for the entry.&nbsp; In old days, file type information is not
    available in FileSystemInfo, and because WinAPI is involved, it's very
    resource consuming to parse the type information of every entry, so type
    information like description and icon are stored in external
    dictionary.&nbsp; External memory based cache does work on desktop
    application but not web services, I decided to include a implementation of
    IEntryTypeInfo, which is cachable in database.<br>
    <br>
    <br>
    <img alt="eti" src="eti_linq.jpg"><br>
    <br>
    The binary digit (byte[]) in the above screenshot is for EntityFramework to
    cache the icon, it's IEntryTypeInfo.LargeIcon return BitmapContainer object,
    which contains BitmapSource that allow you to use in WPF applications. <br>
    <br>
    <h3>Directories</h3>
    ICofeDirectoryInfo support listing and creation sub-entries.&nbsp; For
    Listing, ICofeDirectoryInfoExtension provides a range of
    EnumerateCofeSystemInfos() and GetCofeSystemInfosAsync() methods, which
    takes FilterCollections as parameters.&nbsp; <br>
    <br>
    FilterCollection is used to specify what to return, Although one can use
    FilterCollection.FromParseName(), FromName() etc to produce simple query,
    you can use multiple entry and option filters in a FilterCollections.&nbsp;
    To create those filters one can use FilterCollections.FromFilterString()
    method, which convert a filter string to Entry/OptionFilter objects,&nbsp;
    for examples:<br>
    <br>
    &nbsp; var filterStrList = rootDir.EnumerateCofeSystemInfos(<br>
    &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
    FilterCollections.FromFilterString("filetype:txt
    size:&gt;0").AndProperty(CofeItemProperties.Length, "&lt;100"))<br>
    &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; .Select(e =&gt; e.ParseName);<br>
    <br>
    <img alt="filters" src="filters_linq.jpg"><br>
    <br>
    For IDirectoryInfo, one can use EnumerateFileSystemInfos() and
    GetFileSystemInfosAsync() methods, which, like
    DirectoryInfo.EnumerateFileSystemInfos(), takes file name patterns and
    SearchOptions.<br>
    <h3>Searching </h3>
    In last section we used EntryFE.TryParsePathAsync() to returned one entry,
    use Get/EnumerateCofe/FileSystemInfos to list sub-contents, with the use of
    database, one can skip the hierarchical lookup and search for an entry
    easily.&nbsp; Again, the FilterCollections is important for searching, all
    EntryFilters and OptionFilters are converted to one EntryFilterExpression,
    which is an Expression&lt;Func&lt;CofeSystemInfo, bool&gt;&gt;, it can then
    be used to query for the requested entry in Entity Framework Code First.<br>
    <br>
    <img alt="entryFilterExpression" src="entryFilterExpression_linq.jpg"><br>
    Search is done by using EntryFE.SearchAsync() with a FilterString:<br>
    <br>
    IAutoEntryList el = await EntryFE.SearchAsync("name:testParse* filetype:txt
    size:&lt;100 root:{cofe}");<br>
    foreach (ICofeSystemInfo e in await entryList.GetCofeSystemInfosAsync())<br>
    &nbsp;&nbsp;&nbsp; String.Format("{0} ({1})",&nbsp; e.ParseName,
    e.GetType().Name).Dump();<br>
    <br>
    <img alt="search" src="search_linq.jpg"><br>
    <h3>Organizing entries</h3>
    The information described above are mainly work with entries of original
    hierarchy.&nbsp; User may want to have other way to categorize the items.
    &nbsp; Currently Cofe3 do provide a number of ways to organize entries in
    CofeSystem, tags, links and custom entry list.<br>
    <h4>Tags (Require DB)</h4>
    Tags are hash tags applicable to any database entries, user can use it to
    categorize their entry.&nbsp; It require database because the implementation
    is a one-to-many table that link from items to tags.<br>
    <img alt="tagsSql" src="tags_sql.jpg"><br>
    <br>
    Ones the entry is in database, you can call entries'.AddTags(), RemoveTags()
    and GetTags() extension method (in CofeDB3) to add, remove and access tag
    information, the tags parameter are comma separated, so you can add/remove
    multiple tags at a time.<br>
    Search is done by using the tags property, please noted that comma is not
    supported at this time.<br>
    <br>
    <img alt="tags" src="tags_linq.jpg"><br>
    <br>
    <h4>Links</h4>
    IEntryLink enable any entry to link another entry, by calling
    LinkFE.AddLinks(entry, entryToAddLink) helper method.&nbsp; It tells Cofe3
    to created a new IEntryLink entry that link from first to second
    entry.&nbsp; Below is how the database looks after the command is called.<br>
    <br>
    <img alt="link" src="link_sql.jpg"><br>
    <br>
    LinkFE.EnumerateLinks() method return a list of entry link for a particular
    entry.&nbsp; They are all IEntryLink so to get the actual entry you will
    have to access it's IEntryLink.Entry property, or you can call
    IEntryLink.As&lt;T&gt;() method cast to particular class.<br>
    <br>
    <img alt="link" src="link_cs.jpg"><br>
    <br>
    To remove a link, call the link's DeleteLink() method.&nbsp; If you call the
    DeleteAsync() method, it will delete your actual entry.<br>
    <h4>CustomEntryList </h4>
    The another way to organizing entries is CustomEntryList, like the
    IAutoEntryList, it contains a number of entries, but CustomEntryList is not
    built from a search string.&nbsp; User build the list themselves, by linking
    them.<br>
    <br>
    The difference of CustomEntryList to other directory in Cofe is that, it's
    virtual item in database or memory, and most importantly, it's ListCore
    method returns added links described in last section, while other directory
    returns it's sub-items but not their links.&nbsp; Because these links can be
    cast as ICofeDirectoryInfo using the .As&lt;T&gt;() method, multi-level of
    CustomEntryList can be used as a hierarchical directory structure, based on
    database.<br>
    <br>
    The following examples demonstrate how to create a custom entry list as a
    volume, and how to add items and sub-CustomEntryList and change position of
    particular item:<br>
    <br>
    ICustomEntryList cel = EntryListFE.NewEntryList("cel");&nbsp;&nbsp;&nbsp; <br>
    cel = await cel.MakeParsbleAsync();&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br>
    await cel.AddLinksAsync(file2add);<br>
    IEntryLink subdirLink = cel.CreateLinkFolder("sub");&nbsp;
    &nbsp;&nbsp;&nbsp; <br>
    ICustomEntryList subCel = subdirLink.As&lt;ICustomEntryList&gt;();<br>
    subdirLink.Position = 1; //Change position for an entry.<br>
    await subCel.AddLinksAsync(file2add);<br>
    <br>
    It will look like this in database (noted that those id field should be in
    Guid format)<br>
    <br>
    <table border="1" width="100%">
      <tbody>
        <tr>
          <td><strong>Id</strong></td>
          <td><strong>ParseName</strong></td>
          <td><strong>Label</strong></td>
          <td><strong>Type</strong></td>
          <td><strong>ParentId</strong></td>
          <td><strong>LinkEntryId</strong></td>
          <td><strong>Position</strong></td>
        </tr>
        <tr>
          <td>1</td>
          <td>{cofe}</td>
          <td>{cofe}</td>
          <td>CofeDirectory</td>
          <td>Null</td>
          <td>Null</td>
          <td><br>
          </td>
        </tr>
        <tr>
          <td>2</td>
          <td>{cofe}\file1.txt</td>
          <td>file1.txt</td>
          <td>File</td>
          <td>1</td>
          <td>Null</td>
          <td><br>
          </td>
        </tr>
        <tr>
          <td>3</td>
          <td>{cofe}\file2.txt</td>
          <td>file2.txt</td>
          <td>File</td>
          <td>1</td>
          <td>Null</td>
          <td><br>
          </td>
        </tr>
        <tr>
          <td>4</td>
          <td>{cofe}\file3.txt</td>
          <td>file3.txt</td>
          <td>File</td>
          <td>1</td>
          <td>Null</td>
          <td><br>
          </td>
        </tr>
        <tr>
          <td>5</td>
          <td>{cel}</td>
          <td>{cel}</td>
          <td>CustomEntryList</td>
          <td>Null</td>
          <td>Null</td>
          <td><br>
          </td>
        </tr>
        <tr>
          <td>6</td>
          <td>{cel}\file1.txt</td>
          <td>file1.txt</td>
          <td>EntryLink</td>
          <td>5</td>
          <td>2</td>
          <td>0</td>
        </tr>
        <tr>
          <td>7</td>
          <td>{cel}\file2.txt</td>
          <td>file2.txt</td>
          <td>EntryLink</td>
          <td>5</td>
          <td>3</td>
          <td>2</td>
        </tr>
        <tr>
          <td>8</td>
          <td>{cel}\file3.txt</td>
          <td>file3.txt</td>
          <td>EntryLink</td>
          <td>5</td>
          <td>4</td>
          <td>3</td>
        </tr>
        <tr>
          <td>9</td>
          <td>sub-randomstring</td>
          <td>sub-randomstring</td>
          <td>CustomEntryList</td>
          <td>Null</td>
          <td>Null</td>
          <td><br>
          </td>
        </tr>
        <tr>
          <td>10</td>
          <td>{cel}\sub</td>
          <td>sub</td>
          <td>EntryLink</td>
          <td>5</td>
          <td>9</td>
          <td>1</td>
        </tr>
        <tr>
          <td>11</td>
          <td>sub-randomstring\file1.txt</td>
          <td>file1.txt</td>
          <td>EntryLink</td>
          <td>9</td>
          <td>2</td>
          <td>0</td>
        </tr>
        <tr>
          <td>12</td>
          <td>sub-randomstring\file2.txt</td>
          <td>file2.txt</td>
          <td>EntryLink</td>
          <td>9</td>
          <td>3</td>
          <td>1</td>
        </tr>
        <tr>
          <td>13</td>
          <td>sub-randomstring\file3.txt</td>
          <td>file3.txt</td>
          <td>EntryLink</td>
          <td>9</td>
          <td>4</td>
          <td>2</td>
        </tr>
      </tbody>
    </table>
    <br>
    EntryListFE.FromPathAsync() calls EntryFE.TryParsePath() with lookup
    parameter, can parse custom entry list that made as volume:<br>
    <br>
    <img alt="CustomEntryList" src="cel_linq.jpg"><br>
    <h2>Cofe as a web services</h2>
    <h3>Admin</h3>
    There are two things you can admin right now, user roles and volume
    information. Editing them require the admin permssion, you have to create a
    user, then run Cofe.Web.SecurityConfig.RegisterAsAdmin("your-user-name"); in
    ImmediateWindow.<br>
    <br>
    <img alt="registeradmin" src="admin_webui.jpg"><br>
    <br>
    <h4>User roles</h4>
    <br>
    <img alt="role" src="role_webui.jpg"><br>
    Because the software is designed for home use, I have created a number of
    roles - Admin / Me / FamilyMember / Friend / Other.&nbsp; <br>
    The server address is {server}/admin/permission, you can change the role of
    any registered user except admin.<br>
    <br>
    <h4>Volume</h4>
    Volume are the VolumeInfo in previous section, which defines the root
    directories.&nbsp; The page is just an online version of VolumeFE.MapAsync()
    method, with permission support.&nbsp; Noted that you can set different
    permission for different roles.<br>
    <br>
    <img alt="volume" src="volume_webui.jpg"><br>
    <br>
    <br>
    <h3>RESTful APIs</h3>
    Beside IFileSystemInfo interfaces for desktops, Cofe3 can be accessed
    through RESTfulApis, these APIs are Not SOAP based, it uses HTTP's
    GET/PUT/POST/DELETE action to retrieve and manipulate the cofe system, the
    following is a brief list of implemented APIs :<br>
    <br>
    <table border="1" width="100%">
      <tbody>
        <tr>
          <td><strong>Url path</strong></td>
          <td><strong>Description</strong></td>
        </tr>
        <tr>
          <td>/api/entry</td>
          <td>Access properties, resources and actions of an entry using guid.</td>
        </tr>
        <tr>
          <td>/api/entryList</td>
          <td>Construct a group of entries (not completed).</td>
        </tr>
        <tr>
          <td>/api/parse</td>
          <td style="margin-left: -576px;">Using a parse path, redirect to
            appropriate /api/entry path.</td>
        </tr>
        <tr>
          <td>/api/search</td>
          <td>Search using filter string.</td>
        </tr>
        <tr>
          <td>/api/events</td>
          <td>Return cofe system changes using atom rss feeds.</td>
        </tr>
      </tbody>
    </table>
    <br>
    <h4>Entry APIController - /api/entry</h4>
    Entry api controller is the main way to access particular entries in Cofe3,
    their metadata (properties), resources and actions, these category types are
    described in the following table:<br>
    <br>
    <table border="1" width="100%">
      <tbody>
        <tr>
          <td><strong>Category<br>
            </strong></td>
          <td><strong>PropertyType</strong></td>
          <td><strong>Properties in that category</strong></td>
          <td><strong>Access method</strong></td>
          <td><strong>Description</strong></td>
        </tr>
        <tr>
          <td>WebMetadata</td>
          <td>Property</td>
          <td>CofeProperties.FullParseName (Path) and Label <br>
            CofeItemProperties.Length (size)</td>
          <td>GET - /entry/{guid}<br>
            POST - /entry/{guid} (for update)</td>
          <td>Metadata are cached, can be loaded quickly, represented as text,
            and may be updated.</td>
        </tr>
        <tr>
          <td>WebResources</td>
          <td>Property<br>
            ListAction<br>
            Action</td>
          <td>CofeDirectoryProperties.List <br>
            CofeIconProperties.Icon </td>
          <td>GET - /entry/{guid}/{resourceId}</td>
          <td>Resource takes time to load, or/and not serializable to text, some
            of them can update (using POST resource).</td>
        </tr>
        <tr>
          <td>WebActions</td>
          <td>Action</td>
          <td>CofeProperties.DeleteAsync (Delete) and RefreshAsync (Refresh), <br>
            UserFeedbackProperties.AddTag </td>
          <td>DELETE /entry/{guid} (for delete)<br>
            POST /entry/{guid}/{actionId}</td>
          <td>Action that change the cofesystem.</td>
        </tr>
      </tbody>
    </table>
    <br>
    One may wonder how Cofe3 distinguish properties of different categories,
    actually they are defined exactly in the property, for examples, the
    CofeProperties.RefreshAsync have it's alias set as refresh, and it's an
    action and web action.&nbsp; In other words, they are coded in a manner that
    you can change what to show in what category and what alias any time.<br>
    <img alt="refresh" src="refresh_cs.jpg"><br>
    <br>
    The following is a sample output of an /entry/{guid}, you can see the
    information are formatted in json.&nbsp; Metadata are serialize as json
    property, while Resource and Action are serialize inside the links
    array.&nbsp; <br>
    <img alt="entryDetails" src="entryDetail_json.jpg"><br>
    <br>
    One should use the rel property to find the appropriate link of
    action.&nbsp; The rel property in a link represent it's relation with the
    current entry, except self, which represent the url to access the entry
    itself, other links are Resources and Actions, and the rel is actually it's
    alias.&nbsp; The mediaType property represent the mimetype of what to
    return.&nbsp; There's no indication for distinguish Resources and Actions
    (former require GET, while later require POST action), non what parameter is
    supported at this time.<br>
    <br>
    The entry and it's resource support Http level cache, entry is based on
    LastModified/IfModifiedSince Http header (which loads from
    CacheProperties.LastCachedTimeUtc or LastListTimeUtc), while the resource is
    depended on the properties's WebResourceAttribute.CacheMethod (None/LastModified/MaxAge).<br>
    <h5>Delete - DELETE /api/entry/{guid}</h5>
    This is not advertised in the entry's json, but you can use DELETE action to
    remove an entry.<br>
    <h5>Create - PUT /api/entry</h5>
    <img alt="create" src="create_json.jpg"><br>
    Create entry is done by using PUT action with query string or json data, the
    newly created entry will be returned.&nbsp; For file, user will then have to
    POST on it's stream address (/api/entry/{guid}/stream) with the stream
    content.<br>
    <h5>Form based upload - POST /api/entry/{guid}/upload</h5>
    Some may want to use forms file input to upload files, this is possible
    through POSTing /api/entry/{guid-of-root}/upload or
    /api/parse?path={cofe}&amp;rel=upload.&nbsp; <br>
    <img alt="upload" src="upload_json.jpg"><br>
    <h5>List - GET /api/entry/{guid}/List</h5>
    The List resource return it's sub-contents, they are in json too, with all
    entries inside the "entries" array.&nbsp; To reduce bandwidth, not all
    metadata/resource/actions are displayed in this entry, one have to call it's
    self link for all of them.<br>
    <br>
    <img alt="entryList" src="entryList_json.jpg"><br>
    <br>
    <h5>EntryType - GET /api/entry/{guid}/typeInfo</h5>
    All entries that have type info have it's TypeInfo link exposed.&nbsp; Type
    info are properties that can be shared with many entries, like it's mime and
    file type show below, but most importantly is it's icon resource.&nbsp; You
    can access it's icon in various size using the appropriate link, because the
    icon url of same type of file are same, this allows the cached icons (on
    browser) to be reused.<br>
    <img alt="entryType" src="entryType_json.jpg"><br>
    <br>
    <h5>Update Tag - POST /api/entry/{guid}/addtag</h5>
    This is the same command you can find in a section earlier this article, the
    AddTag() method calls UserFeedbackProperties.AddTag, with a TagNames
    tag.&nbsp; In the web services, you can call the AddTag properties directly,
    and the TagNames parameter is embedded in the query string.&nbsp; Because it
    update the cofe system, UserFeedbackProperties.AddTag is a WebAction and
    POST is required, and the updated entry is returned.&nbsp; The RemoveTag
    command is similar.&nbsp; <br>
    You can also replace&nbsp; the tags property using - POST /entry/{guid} with
    just the tags value in it, CofeWS will try to update every property in your
    upload except id and parsename.<br>
    <br>
    <img alt="addTag" src="addtag_json.jpg"><br>
    <br>
    <h4> EntryList APIController - /api/entryList</h4>
    <h5>Execute action - POST /entryList/{action}</h5>
    Most of the time one have to handle multiple entries instead of one, instead
    of running a request for each entry, one can group them in a json entry list
    with just ids(other properties are discarded), and POST.&nbsp; The
    screenshot above is actually use /entryList instead of /entry.<br>
    <h5>Get Properties - GET /entryList</h5>
    Similar, if you want to get properties of multiple entries, you can create a
    json entry list with ids, the web services will return the completed entry.<br>
    <h5>Update properties - POST /entryList</h5>
    Update properties is done by POSTing an json entry list with ids and
    properties you want to change.<br>
    <br>
    <h4>Parse APIController - /api/parse</h4>
    Using EntryApiController can handle most work of an entry, but to access any
    entry one must have it's guid.&nbsp; ParseApiController, takes a ParseName
    and redirect to respective location in EntryApiController.&nbsp; To reduce
    complexability, parameters are attached in query string:<br>
    <br>
    <table border="1" width="100%">
      <tbody>
        <tr>
          <td>Parameter</td>
          <td>Description</td>
        </tr>
        <tr>
          <td>path</td>
          <td>The entry path to the entry (e.g. path={cofe}\testParse1.txt)</td>
        </tr>
        <tr>
          <td>rel</td>
          <td style="height: 20px;">If you want to call particular resource or
            action, you can specify the rel (relation) to that entry, the list
            of rel can be found in the entrie's links.</td>
        </tr>
      </tbody>
    </table>
    <br>
    Please noted that only GET is fully supported and redirect to appropriate
    url in EntryAPIController. &nbsp; While POST may work, it invoke the
    resource or action directly in ParseAPIController.<br>
    <img alt="parse" src="parse_json.jpg"><br>
    <br>
    <h4>Search APIController - /api/search</h4>
    All metadata of an entry is stored in database and is searchable, and the
    easiest way to search it is to use search string.&nbsp; Search APIController
    takes the filterStr parameter and return a list of entries that matched the
    criteria. <br>
    <img alt="search" src="search_json.jpg"><br>
    Except a few, most properties are supported through PropertyEntryFilter, for
    those property you have to use it's alias (the property without alias, like
    fileattributes shown below, does not work).<br>
    <img alt="filterList" src="filterlist_linq.jpg"><br>
    You can use the paginate (page) filter to control how many to show,
    SortResult (sort) to control how to order your list and searchOption
    (subdir) filter to control whether to search subdirectory.<br>
    <br>
    <h5> Events APIController - GET /api/events</h5>
    When there's a change in cofe system, it generate a events, will is then
    transported to EventHub and then to the feed writter in Cofe3.Web.&nbsp; The
    feed writing code is taken from the book REST in pratice, but I have
    rewrited the code a bit to fit the existing classes in Cofe3.<br>
    <br>
    <img alt="events" src="events_rss.jpg"><br>
    The web services has a thread to update the feed once a while, and when a
    feed is full, new one is created.&nbsp; When you called /api/events, you are
    redirected to the latest feed in memory, or you can call with query string
    page={page#} to specify the page of events you want to show.<br>
    <img alt="events2" src="events2_rss.jpg"><br>
    <h3> Explore</h3>
    <p>The web interface of the explore page (/explore) is fairly basic, I am
      not web designer so this is what I can create in a limited time.&nbsp; </p>
    <p>It's divided into several sections:</p>
    <ul>
      <li>Directory Tree - <br>
        which shows subdirectories, automatically update when directory changed.</li>
      <li>Entry List - <br>
        display all entries in a directory or search, show 20 items per time,
        paginate refresh is activate when user scroll to bottom.<br>
        upload files using drag and drop.</li>
      <li>Search - <br>
        allow user to input a filter string, web interface calls SearchAPIController
        and return result.</li>
      <li>Metadata - <br>
        when one item is selected, display all metadata found in the entry,
        otherwise display "x items selected".&nbsp; <br>
        if the selected items is a photo with GeoTag Exif data, <a href="geotag_webui.jpg">a
          google map</a> is displayed with the location marked.&nbsp; While the
        metadata is generated from the json, the actions are hard-coded.</li>
    </ul>
    <img style="font-family: monospace;" alt="explore" src="explore_webui.jpg"><br>
    <br>
    The underlying components are written with SpineJS, CoffeeScript and
    JQueryUI, they talk with the server only with the WebApi described in above
    sections.<br>
    <br>
    <br>
    <br>
    <br>
    <h2>Conclusion</h2>
    It may be one of the dumpest project ever imagined, to create a web services
    over the web to serve one's all personal data, in a world where online
    company provide api for everyone to store and process any particular types
    of data, with more space than your local hard drive, more fast, for
    free.&nbsp; But I think, no matter how unevil a company is, at the end of
    the day, whether and how to use your personal data, whether to continue
    provide access to certain services or api, how long do they keep the data
    after deletion requested, and most importantly, how often do they change the
    license agreement, is a decision of business, not ethical.&nbsp; <br>
    <br>
    Windows programming and Web programming requires very different set of
    technologies and apis.&nbsp; In today's environment even desktop program
    requires connectivity to the web, so learning web programming is
    inevitable.&nbsp; I've seen other desktop software developers moved to Web
    programming but I always couldn't convince myself to do so, until I
    completed FileExplorer2.<br>
    <br>
    Many factors drive me to switch the development from Cofe1, a pure desktop
    component, to Cofe3, a desktop serving as web services.&nbsp; Among them,
    the interesting book about RESTful web services, the availability of new
    technologies like WCF, MVCWebApi and EntityFramework, the dissertation
    project, and the change of the atmosphere of the online environment are the
    main reasons.&nbsp;&nbsp; <br>
    <br>
    I can never imagine Cofe will become a web services when I was developing
    Cofe1 and DirectoryInfoEx.&nbsp; This takes a long time, throughout the
    processes there was a lot of new technologies learned, and I enjoyed
    that.&nbsp; I hope development can continue and those who need such services
    can benefit.<br>
    <br>
    Although my project is not as feature rich, not as good performance, and
    even not as stable and may not even as secure as the online options,&nbsp;
    but hopefully after reading this article, I can convience you that your own
    home web services can do much more than some kind of file serving server.<br>
    <h2>References</h2>
    <ul>
      <li> <a href="http://restinpractice.com/book/book.html">REST in Practice</a>
        - O'Reilly Media 2010 (Jim Webber, Savas Parastatidis, Ian Robinson)</li>
      <li><a href="http://www.informit.com/store/reengineering-.net-injecting-quality-testability-and-9780321821454">Reengineering
          .NET</a> - Addison-Wesley Professional 2012 (Bradley Irby)</li>
      <li><a href="http://shop.oreilly.com/product/0636920022220.do">Programming
          Entity Framework: Code First </a>- O'Reilly Media 2011 (Lerman, Julia
        and Miller, Rowan)</li>
      <li><a href="http://shop.oreilly.com/product/0636920024309.do">The Little
          Book on CoffeeScript</a> - O'Reilly Media 2012 (MacCaw, Alex)</li>
      <li><a href="http://shop.oreilly.com/product/0636920018421.do">JavaScript
          Web Applications</a> - O'Reilly Media 2011 (MacCaw, Alex)</li>
      <li><a href="http://shop.oreilly.com/product/9781118348468.do">Professional
          ASP.NET MVC 4</a> - Wrox 2012 (Jon Galloway)</li>
      <li><a href="http://www.pearsonhighered.com/educator/product/Understanding-AJAX-Using-JavaScript-to-Create-Rich-Internet-Applications/9780132216357.page">Understanding
          AJAX: Using JavaScript to Create Rich Internet Applications</a> -
        Pearson 2007 (Joshua Eichorn)<br>
        <div style="position: absolute; top: -1999px; left: -1988px;" id="stcpDiv">Joshua
          Eichorn</div>
      </li>
    </ul>
    <ul>
      <li><a href="http://blogs.msdn.com/b/meek/archive/2008/05/02/linq-to-entities-combining-predicates.aspx">LINQ
          to Entities: Combining Predicates</a> - Colin Meek</li>
      <li><a href="http://www.pieterg.com/2012/2/coffeescript-custom-processor-in-aspnet-mvc-4">Coffeescript
          custom-processor in aspnet mvc4</a> - Pieterg</li>
      <li><a href="http://www.strathweb.com/2012/04/rss-atom-mediatypeformatter-for-asp-net-webapi/">RSS
          &amp; Atom MediaTypeFormatter for ASP.NET WebAPI</a> - Filip W</li>
      <li><a href="http://www.minddriven.de/index.php/technology/dot-net/net-winrt-get-custom-attributes-from-enum-value">.NET
          4.5 WinRT: Get custom attributes from enum value</a> - Matthias
        Jauernig</li>
      <li><a href="http://weblogs.asp.net/ricardoperes/archive/2012/03/19/entity-framework-code-first-get-entities-from-local-cache-or-the-database.aspx">EFCodeFirst:
          Get Entities From Local Cache or the Database</a> - Development With A
        Dot</li>
      <li><a href="http://refactormycode.com/codes/945-cached-ienumerable-t">ThreadSafeCachedEnumerable</a>
        - </li>
    </ul>
    <ul>
      <li><a href="stackoverflow.com/questions/5095183/how-would-i-run-an-async-taskt-method-synchronously">How
          would I run an async Task&lt;T&gt; method synchronously?</a> -
        StackOverflow</li>
      <li><a href="http://stackoverflow.com/questions/1122483/c-sharp-random-string-generator">C#
          random string generator</a> - StackOverflow</li>
      <li><a href="http://stackoverflow.com/questions/457316/combining-two-expressions-expressionfunct-bool">Combining
          two expressions (Expression&lt;Func&lt;T, bool&gt;&gt;)</a> -
        StackOverflow</li>
      <li><a href="http://stackoverflow.com/questions/7472013/how-to-create-a-thread-task-with-a-continuous-loop">How
          to create a thread/Task with a continuous loop?</a> - StackOverflow</li>
      <li><a href="http://www.codeproject.com/Articles/115054/Restful-Crud-Operation-on-a-WCF-Service">Restful
          Crud Operation on a WCF Service</a> - CodeProject</li>
      <li><a href="http://www.codeproject.com/Articles/104929/Bitmap-to-BitmapSource">Bitmap
          to BitmapSource</a> - CodeProject </li>
      <li><a href="http://www.codeproject.com/Articles/10890/Fastest-C-Case-Insenstive-String-Replace">Fastest
          C# Case Insenstive String Replace</a> - CodeProject</li>
    </ul>
    <ul>
      <li><a href="http://www.asp.net/web-api/overview/working-with-http/sending-html-form-data,-part-2">Sending
          HTML Form Data</a> - ASP.Net</li>
      <li><a href="http://msdn.microsoft.com/en-us/data/jj591621.aspx">Code
          First Migrations</a> - MSDN</li>
    </ul>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
  </body>
</html>
